/*
 * Copyright (c) 2017. Xi'an iRain IOT Technology service CO., Ltd (ShenZhen). All Rights Reserved.
 */

package com.parkingwang.keyboard.view;

import com.parkingwang.keyboard.*;
import com.parkingwang.keyboard.annotation.NonNull;
import com.parkingwang.keyboard.engine.*;
import com.parkingwang.keyboard.utils.VLog;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.TouchEvent;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author 黄浩杭 (huanghaohang@parkingwang.com)
 * @author 陈永佳 (chenyongjia@parkingwang.com)
 */
public class KeyboardView extends DirectionalLayout implements Component.DrawTask, Component.EstimateSizeListener {
    private HiLogLabel hiLogLabel = new HiLogLabel(HiLog.LOG_APP, 0x78002, KeyboardView.class.getSimpleName());
    private final String TAG = getClass().getSimpleName();

    private final Paint mDividerPaint = new Paint();
    private final List<OnKeyboardChangedListener> mKeyboardChangedListeners = new CopyOnWriteArrayList<>();
    private final KeyboardEngine mKeyboardEngine = new KeyboardEngine();
    private final KeyViewCacheHelper mKeyCacheHelper = new KeyViewCacheHelper();
    private KeyboardEntry keyboard;
    private int mRowSpace;
    private int mDefaultKeyHeight;
    private boolean mShowBubble = true;
    private Text.TextSizeType textSizeType;
    private float mCNTextSize;
    private float mENTextSize;
    private TouchEvent mLastEvent;

    // 缓存当前状态，用于切换“更多”与“返回”的状态
    private String mStashedNumber;
    private int mStashedIndex;
    private NumberType mStashedNumberType;

    //按下气泡文字的颜色
    private int mBubbleTextColor = -1;

    //确定键的背景颜色
    private StateElement mOkKeyTintColor;

    private final ClickedListener mOnKeyPressedListener = new ClickedListener() {

        @Override
        public void onClick(Component v) {
            if (!(v instanceof KeyView)) {
                return;
            }
            final KeyEntry key = ((KeyView) v).getBoundKey();
            onKeyPressed(key);
        }
    };

    public KeyboardView(Context context) {
        this(context, null);
    }

    public KeyboardView(Context context, @Nullable AttrSet attrs) {
        super(context, attrs);
        addDrawTask(this);
        setEstimateSizeListener(this);
        mBubbleTextColor = AttrUtils.getColorValueByAttr(attrs, AttrFieldName.KEYBOARDVIEW_PWBBUBBLECOLOR, new Color(Color.getIntColor("#ff418AF9"))).getValue();
        mOkKeyTintColor = getMOKeyTintColor();

        mDefaultKeyHeight =  (int) HmsResourcesManager.getFloatValueByResources(context, ResourceTable.Float_pwk_keyboard_key_height);;
        HiLog.info(hiLogLabel,"KeyboardView mDefaultKeyHeight = "+mDefaultKeyHeight);
        setOrientation(DirectionalLayout.VERTICAL);

        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(RgbColor.fromArgbInt(HmsResourcesManager.getColorValueByResources(context, ResourceTable.Color_pwk_keyboard_background)));
        setBackground(shapeElement);

        final int dividerColor = HmsResourcesManager.getColorValueByResources(context, ResourceTable.Color_pwk_keyboard_divider);
        mDividerPaint.setColor(new Color(dividerColor));

        final int leftPadding = (int) HmsResourcesManager.getFloatValueByResources(context, ResourceTable.Float_pwk_keyboard_padding_left);
        final int topPadding = (int) HmsResourcesManager.getFloatValueByResources(context, ResourceTable.Float_pwk_keyboard_padding_top);
        final int rightPadding = (int) HmsResourcesManager.getFloatValueByResources(context, ResourceTable.Float_pwk_keyboard_padding_right);
        final int bottomPadding = (int) HmsResourcesManager.getFloatValueByResources(context, ResourceTable.Float_pwk_keyboard_padding_bottom);

        setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
        setClipEnabled(false);

        mCNTextSize = HmsResourcesManager.getFloatValueByResources(context, ResourceTable.Float_pwk_keyboard_key_cn_text_size);
        mENTextSize = HmsResourcesManager.getFloatValueByResources(context, ResourceTable.Float_pwk_keyboard_key_en_text_size);
        textSizeType = Text.TextSizeType.PX;
        setClickedListener(v -> {
            // 避免键盘面板的点击事件往下层传
        });
    }

    /**
     * 获取颜色状态值
     *
     * @return 颜色状态值
     */
    private StateElement getMOKeyTintColor() {
        StateElement stateElement = new StateElement();
        int[] disableStateList = new int[]{ComponentState.COMPONENT_STATE_DISABLED};
        ShapeElement disableElement = new ShapeElement();
        disableElement.setRgbColor(RgbColor.fromArgbInt(Color.getIntColor("#090909")));
        stateElement.addState(disableStateList, disableElement);

        int[] pressStateList = new int[]{ComponentState.COMPONENT_STATE_PRESSED};
        ShapeElement pressElement = new ShapeElement();
        pressElement.setRgbColor(RgbColor.fromArgbInt(HmsResourcesManager.getColorValueByResources(getContext(), ResourceTable.Color_pwk_keyboard_primary_dark_color)));
        stateElement.addState(pressStateList, pressElement);

        int[] emptyStateList = new int[]{ComponentState.COMPONENT_STATE_EMPTY};
        ShapeElement emptyElement = new ShapeElement();
        emptyElement.setRgbColor(RgbColor.fromArgbInt(HmsResourcesManager.getColorValueByResources(getContext(), ResourceTable.Color_pwk_keyboard_primary_color)));
        stateElement.addState(emptyStateList, emptyElement);
        return stateElement;
    }

    public void setBubbleTextColor(int bubbleTextColor) {
        mBubbleTextColor = bubbleTextColor;
    }

    public void setOkKeyTintColor(StateElement okKeyTintColor) {
        mOkKeyTintColor = okKeyTintColor;
    }

    public KeyboardEngine getKeyboardEngine() {
        return mKeyboardEngine;
    }

    /**
     * 更新车牌键盘。
     * 此操作会触发KeyboardCallback回调。
     *
     * @param number          当前已输入的车牌
     * @param showIndex       当前正在修改的车牌字符所在的序号
     * @param showMore        是否显示“更多”按钮
     * @param fixedNumberType 指定车牌号类型
     */
    public void update(@NonNull final String number, final int showIndex, final boolean showMore, final NumberType fixedNumberType) {
        mStashedNumber = number;
        mStashedIndex = showIndex;
        mStashedNumberType = fixedNumberType;
        // 更新键盘布局
        keyboard = mKeyboardEngine.update(number, showIndex, showMore, fixedNumberType);
        renderLayout();
        // 触发键盘变更回调
        try {
            for (OnKeyboardChangedListener listener : mKeyboardChangedListeners) {
                listener.onKeyboardChanged(keyboard);
            }
        } catch (Exception e) {
            HiLog.info(hiLogLabel, "On keyboard changed exception = " + e);
        }
    }

    private void onKeyPressed(KeyEntry key) {
        switch (key.keyType) {
            case FUNC_OK:
                for (OnKeyboardChangedListener l : mKeyboardChangedListeners) {
                    l.onConfirmKey();
                }
                break;

            case FUNC_DELETE:
                for (OnKeyboardChangedListener l : mKeyboardChangedListeners) {
                    l.onDeleteKey();
                }
                break;

            default:
            case GENERAL:
                for (OnKeyboardChangedListener l : mKeyboardChangedListeners) {
                    l.onTextKey(key.text);
                }
                break;

            case FUNC_MORE:
                update(mStashedNumber, mStashedIndex, true, mStashedNumberType);
                break;

            case FUNC_BACK:
                update(mStashedNumber, mStashedIndex, false, mStashedNumberType);
                break;

        }
    }

    /**
     * 设置键盘状态回调接口
     *
     * @param callback 回调接口
     */
    public void addKeyboardChangedListener(@NonNull OnKeyboardChangedListener callback) {
        mKeyboardChangedListeners.add(callback);
    }

    /**
     * 移除键盘状态回调接口
     *
     * @param callback 回调接口
     */
    public void removeKeyboardChangedListener(@NonNull OnKeyboardChangedListener callback) {
        mKeyboardChangedListeners.remove(callback);
    }

    /**
     * 设置是否显示浮动提示View
     *
     * @param showBubble 是否显示
     */
    public void setShowBubble(boolean showBubble) {
        mShowBubble = showBubble;
    }

    /**
     * 设置按键的数字或字母大小
     *
     * @param textSize 按键数字或字母大小，单位为sp
     * @since 0.6
     */
    public void setCNTextSize(float textSize) {
        setCNTextSize(Text.TextSizeType.FP, textSize);
    }

    /**
     * 设置按键的数字或字母大小
     *
     * @param unit     单位
     * @param textSize 大小的值
     */
    public void setCNTextSize(Text.TextSizeType unit, float textSize) {
        mCNTextSize = textSize;
        textSizeType = unit;
    }

    /**
     * 设置按键的汉字大小
     *
     * @param textSize 按键汉字大小，单位为sp
     * @since 0.6
     */
    public void setENTextSize(float textSize) {
        setENTextSize(Text.TextSizeType.FP, textSize);
    }

    /**
     * 设置按键的中文字体大小
     *
     * @param unit     单位
     * @param textSize 大小的值
     */
    public void setENTextSize(Text.TextSizeType unit, float textSize) {
        textSizeType = unit;
        mENTextSize = textSize;
    }

    public void renderLayout() {
        if(keyboard == null){
            return;
        }
        // 以第一行的键盘数量为基准
        final int maxColumn = keyboard.layout.get(0).size();
        final int rowSize = keyboard.layout.size();
        mKeyCacheHelper.recyclerKeyRows(this, rowSize);

        for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
            List<KeyEntry> keyEntryRow = keyboard.layout.get(rowIndex);
            KeyRowLayout rowLayout = (KeyRowLayout) getComponentAt(rowIndex);
            rowLayout.setMaxColumn(maxColumn);
            final int columnSize = keyEntryRow.size();
            int funKeyCount = 0;
            for (KeyEntry keyEntry : keyEntryRow) {
                HiLog.info(hiLogLabel,"renderLayout entry = "+keyEntry);
                if (keyEntry.isFunKey) {
                    funKeyCount++;
                }
            }
            rowLayout.setFunKeyCount(funKeyCount);

            mKeyCacheHelper.recyclerKeyViewsInRow(rowLayout, columnSize, mOnKeyPressedListener);
            for (int i = 0, size = keyEntryRow.size(); i < size; i++) {
                KeyEntry key = keyEntryRow.get(i);
                KeyView keyView = (KeyView) rowLayout.getComponentAt(i);

                if (mBubbleTextColor != -1) {
                    keyView.setBubbleTextColor(mBubbleTextColor);
                }

                if (key.keyType == KeyType.FUNC_OK && mOkKeyTintColor != null) {
                    keyView.setOkKeyTintColor(mOkKeyTintColor);
                }
                keyView.bindKey(key);
                if (key.keyType == KeyType.FUNC_DELETE) {
                    keyView.setText("");
                } else {
                    keyView.setText(key.text);
                }

                if (Texts.isEnglishLetterOrDigit(key.text)) {
                    keyView.setTextSize((int) mENTextSize, textSizeType == null ? Text.TextSizeType.PX : textSizeType);
                } else {
                    keyView.setTextSize((int) mCNTextSize, textSizeType == null ? Text.TextSizeType.PX : textSizeType);
                }
                keyView.setShowBubble(mShowBubble);
                keyView.setEnabled(key.enabled);

                //if(keyView.getBoundKey().keyType != KeyType.GENERAL){
                    keyView.invalidate();
                //}
            }
        }

    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        onMeasure();
        RectFloat rectFloat = new RectFloat(0, 0, getWidth(), 2);
        canvas.drawRect(rectFloat, mDividerPaint);
    }

    private void onMeasure() {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            ComponentContainer componentAt = (ComponentContainer) getComponentAt(i);
            componentAt.setClickedListener(component -> HiLog.info(hiLogLabel,"onMeasure click rowLayout = "+component));
        }
    }

    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        VLog.d(TAG,"onEstimateSize heightMode = "+heightMode);
        final int rows = getChildCount();
        if (heightMode == MeasureSpec.NOT_EXCEED) {
            for (int i = 0; i < rows; i++) {
                getComponentAt(i).setHeight(mDefaultKeyHeight);
            }
        } else if (heightMode == MeasureSpec.PRECISE) {
            if (rows > 0) {
                for (int i = 0; i < rows; i++) {
                    getComponentAt(i).setHeight(mDefaultKeyHeight);
                }
            }
        }
        return false;
    }
}
